package Thread_;

import java.util.PriorityQueue;

/**
 * 模拟实现 阻塞队列
 * 通过数组实现
 * 创建一个  循环数组队列
 */
class MyPriorityQueue{
    public String[] elem=null;
    public MyPriorityQueue(int capacity){
        elem=new String[capacity];
    }
    public int head=0;//指向队头
    public int tail=0;//指向队尾
    public int size=0;//数组中元素个数

    Object locker=new Object();

    public void put(String s) throws InterruptedException {
        synchronized(locker){//添加元素时，涉及到多处的修改操作，
                            // 在多线程下，可能会引发线程安全问题，要加锁
            while(size==elem.length){//队列满，就进入阻塞状态
                locker.wait();
            }
            elem[tail]=s;
            tail++;
            if(tail==elem.length){//tail走到队尾时，让其回到对头，从新开始
                tail=0;
            }
            //tail=tail%elem.length();//这个与上面代码功能相同
            size++;
            locker.notify();//入队一个元素后，唤醒被阻塞的判断队列为空的take线程（也有可能唤醒的时别的线程）
        }
    }
    public String take() throws InterruptedException {
        synchronized(locker){
            while(size==0){//队列为空时，阻塞等待
                locker.wait();
            }
            String s=elem[head];
            head++;
            size--;
            if(head>=elem.length){//head走到队尾时，让其回到对头，循环起来
                head=0;
            }
            locker.notify();//取出元素后，唤醒被阻塞的判断已满的插入线程（也有可能唤醒的时别的线程）
            return s;
        }
    }
}

/**
 * 模拟实现阻塞队列
 * 通过 循环数组队列 实现
 *
 * head:指向队头
 * tail:指向队尾
 * size:记录数组中元素的个数
 */
class MyPriorityQueue1{
    public String[] elem=null;
    public int head;
    public int tail;
    public int size;//数组中元素个数

    public MyPriorityQueue1(int capacity){
        elem=new String[capacity];
    }

    /**
    //在多线程下 往数组中放元素
    //因put方法中，整个过程都有对数据的修改，可能存在线程安全问题
    //需要将整个方法打包成一个原子
     * @param s 要插入的元素
     * @throws InterruptedException
     */
    Object locker=new Object();
    public void put(String s) throws InterruptedException {
        synchronized(locker){//put方法中，整个过程都有对数据的修改，可能存在线程安全问题
                            //需要将整个方法打包成一个原子
            //这里要将if改成while，多次判断
            //防止多线程下，一个线程中 put在wait阻塞时，而释放锁，唤醒另一个线程的put
            //这里设置多次判断，防止唤醒后，值已经被修改，引发线程安全问题
            while(size==elem.length){
                //队列已满,再次插入元素是，执行阻塞功能
                locker.wait();
            }
            elem[tail]=s;//尾插到队列中
            tail++;
            if(tail==elem.length){
                tail=0;//当tail走到队尾时,让他从头开始,循环起来
            }
            size++;
            //放入元素后，队列一定不再是空，唤醒 因队列为空而阻塞的take线程
            //(这里也有可能唤醒别的线程）
            locker.notify();
        }
    }
    //从数组中取元素

    /**
     *   在多线程下 从数组中取元素
     *     take方法中，整个过程都有对数据的修改，可能存在线程安全问题
     *     也需要将 整个方法 打包成一个 原子
     * @return
     * @throws InterruptedException
     */
    public String take() throws InterruptedException {
        synchronized(locker){
            //这里也要修改，和put中修改原因相同
            while(size==0){
                //队列为空,再取元素时,执行阻塞功能
               locker.wait();
            }
            String s=elem[head];
            head++;
            if(head==elem.length){
                head=0;//当head走到队尾时,让他从头开始,循环起来
            }
            size--;
            //取出元素后，队列一定不再是满，唤醒 因队列已满而阻塞的put线程
            //(这里也有可能唤醒别的线程）
            locker.notify();
            return s;
        }
    }
}
public class Thread19 {
    public static void main2(String[] args) {
        MyPriorityQueue queue = new MyPriorityQueue(1000);

        Thread t1=new Thread(()->{
            int n=1;
            while(true){
                try {
                    queue.put(n+" ");
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                try {
                    Thread.sleep(500);//让放元素比较慢
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("放入元素："+n);
                n++;
            }
        });
        Thread t2=new Thread(()->{
            while(true){
                String elem= null;
                try {
                    elem = queue.take();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
//                try {
//                    Thread.sleep(500);//让取元素比较慢
//                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
//                }
                System.out.println("取出元素：   "+elem);
            }
        });
        t1.start();
        t2.start();
//        System.out.println();
    }
    public static void main(String[] args) throws InterruptedException {
        MyPriorityQueue1 queue = new MyPriorityQueue1(100);
        queue.put("aa");
        queue.put("bb");
        queue.put("cc");
        queue.put("dd");
        System.out.println(queue.take());
        System.out.println(queue.take());
        System.out.println(queue.take());
        System.out.println(queue.take());
    }
}
